#include "CommonFunc.h"
#include "DesktopCapture.h"
#include "BitmapSocketViaFile.h"

#include "boost/tuple/tuple.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/shared_array.hpp"
#include "boost/lambda//bind.hpp"

#include "protocal.h"
#include "CommandArgs.h"
#include "MessagePack.h"
#include "MessageCommand.h"

#include <gdiplus.h>

#include <stdexcept>
#include <fstream>

using namespace Gdiplus;
using namespace std;
using boost::tuple;
using std::runtime_error;
using boost::shared_ptr;

class MyCloseHdc
{
public:
	typedef void result_type;
	void operator()(HDC *handle){
		CloseHandle(*handle);
	}
};

static HBITMAP Capture(HDC hdcScreen,HDC hdcCompatible, HBITMAP hbmScreen)
{


	int width,height;
	width = GetDeviceCaps(hdcScreen, HORZRES);
	height = GetDeviceCaps(hdcScreen, VERTRES);

	//Copy color data for the entire display into a 
	//bitmap that is selected into a compatible DC. 



	if (!BitBlt(hdcCompatible, 
		0,0, 
		width,height,
		hdcScreen, 
		0,0, 
		SRCCOPY)) 
	{
		throw runtime_error("BitBlt error");
	}


	return hbmScreen;
}

DWORD WINAPI CaptureFunc(LPVOID arg)
{
	tuple<int,int> *info = reinterpret_cast<tuple<int,int> *>(arg);
	int socketFd = info->get<0>();
	int framePerSec = info->get<1>();
	delete info;

	GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);




	HDC hdcScreen,hdcCompatible;
	HBITMAP hScreenBitmap;
	hdcScreen = CreateDC(L"DISPLAY", NULL, NULL, NULL); 
	hdcCompatible = CreateCompatibleDC(hdcScreen);

	int width,height;
	width = GetDeviceCaps(hdcScreen, HORZRES);
	height = GetDeviceCaps(hdcScreen, VERTRES);

	// Create a compatible bitmap for hdcScreen. 

	hScreenBitmap = CreateCompatibleBitmap(hdcScreen, 
		width, 
		height); 

	if (hScreenBitmap == 0) 
	{
		PrintErrorMessage("Create compatible bitmap error");
		return -1;
	}

	// Select the bitmaps into the compatible DC. 

	if (!SelectObject(hdcCompatible, hScreenBitmap)) 
	{
		PrintErrorMessage("Compatible Bitmap Selection error");
		return -1;
	}

	BitmapSocketAdaptorFactory adaptorFactory;
	boost::shared_ptr<BitmapSocketAdaptor> bitmapSocketAdaptor = adaptorFactory.createAdaptor("stream");
	while (true)
	{
		try{
			SendData(socketFd, reinterpret_cast<const char*>(&DESKTOP_ScreenBitmap), 4);

			hScreenBitmap = Capture(hdcScreen, hdcCompatible, hScreenBitmap);
			Bitmap screenBitmap(hScreenBitmap, NULL);
			bitmapSocketAdaptor->SaveBitmapToSocket(screenBitmap, socketFd);

		}catch(const runtime_error& e){
			PrintErrorMessage(e.what());
			return -1;
		}

		Sleep(1000 / framePerSec);
	}
	
	return 0;
	
}

DWORD WINAPI MessageTrans(LPVOID arg)
{
	int socketFd = (int) arg;
	MessagePack messagePack(socketFd);
	unsigned long commandType;

	while(true)
	{
		if(!ReceiveData(socketFd, reinterpret_cast<char*>(&commandType), sizeof(commandType), sizeof(commandType) ))
		{
			PrintErrorMessage("Receive command type error in message transfer thread");
			return -1;
		}
		if(commandType == DESKTOP_Message)
		{
			try{			
				boost::shared_ptr<MessageCommand> messageCommand = messagePack.createCmdFromSocket();
				messageCommand->execute();
			}catch(const std::exception& e)
			{
				PrintErrorMessage(e.what());
				return -1;
			}

		}
	}
	return 0;
}

DesktopCapture::DesktopCapture(const char *serverIp, int port, int framePerSec)
:m_FramePerSec(framePerSec)
{
	m_SocketFd = socket(AF_INET, SOCK_STREAM, 0);
	if (m_SocketFd == -1)
	{
		throw std::runtime_error("Can't create socket");
	}

	sockaddr_in socketAddr;
	socketAddr.sin_addr.s_addr = inet_addr(serverIp);
	socketAddr.sin_port = port;
	socketAddr.sin_family = AF_INET;

	bool succ = RetryConnect(m_SocketFd, &socketAddr);
	if( !succ ) 
	{
		throw std::runtime_error("Can't connect to the server");
	}

}

void DesktopCapture::startCapture()
{

	tuple<int,int>* info = new tuple<int,int>(m_SocketFd, m_FramePerSec);
	m_CapThread = CreateThread(
		NULL,
		0,
		CaptureFunc,
		info,
		0,
		NULL);
	if(m_CapThread == NULL)
	{
		throw std::runtime_error("Can't start capture thread");
	}
	m_MessThread = CreateThread(
		NULL,
		0,
		MessageTrans,
		(LPVOID)m_SocketFd,
		0,
		NULL);
	if(m_MessThread == NULL)
	{
		throw std::runtime_error("Can't start message transfer thread");
	}
}